package utils

/***
  对象管理器
    可以查看test
*/

var (
	objFactoryGroup GSyncMap
)

type UnChangedErr struct {
}

func (e *UnChangedErr) Error() string { return "UnChangedErr" }

var (
	EUnChangedErr *UnChangedErr = &UnChangedErr{}
)

type IConfigSet interface {
	SetConfig(conf interface{}) error
}

const (
	NOTIFY_TYPE_RELEASE = 1
	NOTIFY_TYPE_SETCONF = 2
)

/*
*

	工厂通知
	  notifytype: 1: 对象不再使用 objtype改变, 或者objPojo关闭
*/
type IObjectNotify interface {
	OnObjectNotify(sender interface{}, notifytype int, data interface{}) (rval interface{}, err error)
}

type ObjPojo struct {
	id      string
	objtype string
	obj     interface{} // 插件对象，如果被释放后，再次激活会创建新额插件对象，原来对象会被释放
	Tag     int
}

func ErrIsUnChangedErr(err error) bool {
	if err == nil {
		return false
	}
	_, ok := err.(*UnChangedErr)
	return ok
}

func TrySetConfig(obj, conf interface{}) (bool, error) {
	if intf, ok := obj.(IConfigSet); ok {
		err := intf.SetConfig(conf)
		return true, err
	} else {
		return false, nil
	}
}

func TryObjectNotify(obj interface{}, sender interface{}, notifytype int, data interface{}) (bool, interface{}, error) {
	if obj == nil {
		return false, nil, nil
	}
	if intf, ok := obj.(IObjectNotify); ok {
		rval, err := intf.OnObjectNotify(sender, notifytype, data)
		return true, rval, err
	} else {
		return false, nil, nil
	}
}

func (this *ObjPojo) GetId() string {
	return this.id
}

func (this *ObjPojo) CheckInnerObject(objtype string, onreleasefunc func(obj interface{}), oncreatefunc func(obj interface{})) (err error) {
	if this.obj == nil {
		this.objtype = objtype
	} else if this.objtype == objtype { // 一样直接退出
		return nil
	} else {
		this.objtype = objtype
		if onreleasefunc != nil {
			onreleasefunc(this.obj)
		}
		this.obj = nil
	}

	if this.objtype == "NONE" || len(this.objtype) == 0 { // 不在创建
		return nil
	}

	newObj, err := CreateInstance(this.objtype, this.id)
	if err != nil {
		return err
	}
	this.obj = newObj
	if oncreatefunc != nil {
		oncreatefunc(this.obj)
	}
	return nil
}

func (this *ObjPojo) ReloadConfEx(objtype string, conf interface{}, onreleasefunc func(obj interface{}), oncreatefunc func(obj interface{})) (err error) {
	if this.obj == nil {
		this.objtype = objtype
	} else {
		if this.objtype == objtype {
			ok, err := TrySetConfig(this.obj, conf)
			if err != nil {
				return err
			}

			if !ok {
				_, _, err = TryObjectNotify(this.obj, this, NOTIFY_TYPE_SETCONF, conf)
				return err
			}
			return nil
		}
		// 类型不一样, 重新创建
		this.objtype = objtype
	}

	this.ReleaseObj(onreleasefunc)

	if this.objtype == "NONE" || len(this.objtype) == 0 { // 不在创建
		return nil
	}

	sender, err := CreateInstance(this.objtype, this.id)
	if err != nil {
		return err
	}

	{
		ok, err := TrySetConfig(sender, conf)
		if err != nil && !ErrIsUnChangedErr(err) {
			return err
		}

		if !ok {
			_, _, err := TryObjectNotify(sender, this, NOTIFY_TYPE_SETCONF, conf)
			if err != nil && !ErrIsUnChangedErr(err) {
				return err
			}
		}
	}
	this.obj = sender
	if oncreatefunc != nil {
		oncreatefunc(this.obj)
	}
	return nil
}

func (this *ObjPojo) ReleaseObj(onreleasefunc func(obj interface{})) (err error) {
	if this.obj != nil {
		// 通知之前对象不再使用了
		ok, _, _ := TryObjectNotify(this.obj, this, NOTIFY_TYPE_RELEASE, nil)
		if ok {
			if onreleasefunc != nil {
				onreleasefunc(this.obj)
			}
		} else {
			TryClose(this.obj) // 执行关闭
		}
		this.obj = nil
	}
	return nil
}

func (this *ObjPojo) ReloadConf(objtype string, conf interface{}) (err error) {
	return this.ReloadConfEx(objtype, conf, nil, nil)
}

func (this *ObjPojo) GetObject() interface{} {
	return this.obj
}

func CheckGetObjPojo(group, id string) *ObjPojo {
	gp := objFactoryGroup.CheckGetGroup(group)
	return CheckGetObjPojoByGroup(gp, id)
}

func CheckGetObjPojoByGroup(group *GSyncMapGroup, id string) *ObjPojo {
	val := group.LoadOrStoreFunc(id, func() interface{} {
		return &ObjPojo{id: id}
	}, nil)
	if val == nil {
		return nil
	} else {
		return val.(*ObjPojo)
	}
}

func FindObjPojoByGroup(group *GSyncMapGroup, id string) *ObjPojo {
	val := group.Get(id, nil)
	if val == nil {
		return nil
	} else {
		return val.(*ObjPojo)
	}
}

func CheckGetObjGroup(groupid string) *GSyncMapGroup {
	return objFactoryGroup.CheckGetGroup(groupid)
}

func RangeGroupPojos(groupid string, fn func(key, value interface{}) bool) {
	gp := CheckGetObjGroup(groupid)
	gp.Range(fn)
}
